//
//  SWGrid.m
//  White Detector Grid
//
//  Created by David Langford on 6/05/09.
//  Copyright 2009 Sentient Worlds. All rights reserved.
//
#import <OpenGL/CGLMacro.h>
#import "SWGrid.h"
#import "SWCell.h"

@implementation SWGrid


@synthesize numberOfColumns
			, numberOfRows
			, widthInPixels
			, heightInPixels
			, cells
			, widthNormalised
			, heightNormalised
			, xOffsetNormalised
			, yOffsetNormalised	
			, xOffsetInPixels
			, yOffsetInPixels
			, destinationWidthInUnits
			, destinationHeightInUnits
			, destinationXOffsetInUnits
			, destinationYOffsetInUnits;

- (id) initWithNumberOfColumns: (double)columns 
				  numberOfRows: (double)rows
					imageToUse: (id<QCPlugInInputImageSource>)theImage
				   withContext: (id<QCPlugInContext>)theContext
				  widthPercent: (double)theWidthPercent
				 heightPercent: (double)theHeightPercent
				xOriginPercent: (double)theXOriginPercent
				yOriginPercent: (double)theYOriginPercent
	   destinationWidthInUnits: (double)theDestinationWidthInUnits
	  destinationHeightInUnits: (double)theDestinationHeightInUnits
				xOffsetInUnits: (double)theDestinationXOffsetInUnits
				yOffsetInUnits: (double)theDestinationYOffsetInUnits
{
	// Validate arguments
	if (columns < 1 // need columns and rows
		|| rows < 1
		|| !theImage // need an image
		|| theWidthPercent <= 0 // Need some width and height
		|| theHeightPercent <= 0
		|| theXOriginPercent < 0 // Can't start the grid in negative
		|| theYOriginPercent < 0
		|| theDestinationWidthInUnits == 0	// Remove possible divide by 0 error
		|| theDestinationHeightInUnits == 0)
	{
		[self release];
		return nil;
	}
	
	// Cap arguments if needed
	if (theWidthPercent + theXOriginPercent > 100)
		theWidthPercent = 100 - theXOriginPercent;
	if (theHeightPercent + theYOriginPercent > 100)
		theHeightPercent = 100 - theYOriginPercent;
	
	// initialise
	if (self = [super init])
	{
		self.numberOfColumns = (int) columns;
		self.numberOfRows = (int) rows; 
		self.widthNormalised = theWidthPercent/100.0;
		self.heightNormalised = theHeightPercent/100.0;
		self.xOffsetNormalised = theXOriginPercent/100.0;
		self.yOffsetNormalised = theYOriginPercent/100.0;
		self.destinationWidthInUnits = theDestinationWidthInUnits;
		self.destinationHeightInUnits = theDestinationHeightInUnits;
		self.destinationXOffsetInUnits = theDestinationXOffsetInUnits;
		self.destinationYOffsetInUnits = theDestinationYOffsetInUnits;
		self.cells = [self generateCellsUsingImage:theImage andContext:theContext];
	}
	
	return self;
}

// Free any memory we have used
- (void) dealloc
{
	// Release the cells
	[self.cells release];
	[super dealloc];
}

- (NSArray*) generateCellsUsingImage:(id<QCPlugInInputImageSource>)imageToUse
						  andContext:(id<QCPlugInContext>)theContext
{
	// Not to sure what this does exactly, but it gave me a couple more fps
	CGColorSpaceRef					colorSpace;
	colorSpace = (CGColorSpaceGetModel([imageToUse imageColorSpace]) == kCGColorSpaceModelRGB ? [imageToUse imageColorSpace] : [theContext colorSpace]);

	// Get the correct pixelformat
	NSString* pixelFormat;
	#if __BIG_ENDIAN__
		pixelFormat = QCPlugInPixelFormatARGB8; // This is what the HistogramOperation tutorial uses
	#else
		pixelFormat = QCPlugInPixelFormatBGRA8; // This is my iMac
	#endif
	
	// Get a buffer representation from the image (This is an expensive call, keep outside loop)
	if (![imageToUse lockBufferRepresentationWithPixelFormat:pixelFormat
												  colorSpace:colorSpace
												   forBounds:[imageToUse imageBounds]])
		
		return NO;
	
	// store the width and height of the part of the image we will analyse, then find cell width and height. Also find the offset
	self.widthInPixels = [imageToUse bufferPixelsWide]*self.widthNormalised;
	self.heightInPixels = [imageToUse bufferPixelsHigh]*self.heightNormalised;
	self.xOffsetInPixels = [imageToUse bufferPixelsWide]*self.xOffsetNormalised;
	self.yOffsetInPixels = [imageToUse bufferPixelsHigh]*self.yOffsetNormalised;
	double cellWidthInPixels = self.widthInPixels/self.numberOfColumns;
	double cellHeightInPixels = self.heightInPixels/self.numberOfRows;
	
	// Generate weight for each cell and store the values
	NSMutableArray* newCells = [NSMutableArray arrayWithCapacity: self.numberOfColumns];
	Boolean error = NO;
	for (NSInteger rowIndex=0; rowIndex<self.numberOfRows; rowIndex++)
	{
		// This will hold an row of cells
		NSMutableArray* rowOfNewCells = [[NSMutableArray alloc] init];
		
		// Loop through each cell for this row
		for (NSInteger columnIndex=0; columnIndex<self.numberOfColumns; columnIndex++)
		{
			// Create the initial position for this cell
			int xPositionInPixels = columnIndex*cellWidthInPixels+self.xOffsetInPixels;
			int yPositionInPixels = rowIndex*cellHeightInPixels+self.yOffsetInPixels;
			
			// Create the cell
			SWCell* newCell = [[SWCell alloc] initWithXPositionAt:xPositionInPixels
													  yPositionAt:yPositionInPixels														
													widthInPixels:cellWidthInPixels
												   heightInPixels:cellHeightInPixels];
			
			// Tell the cell to generate its weight
			Boolean success = [newCell generateWeightFromImage:imageToUse 
													 inContext:theContext];
			
			// Record if error happened
			if (!success)
			{
				NSLog(@"error in generating the weight");
				error = YES;
			}
			// Store this data in an array that we can return
			[rowOfNewCells insertObject:[newCell asNSArrayWithDestinationWidthInPixels:[imageToUse bufferPixelsWide]
														 destinationAreaHeightInPixels:[imageToUse bufferPixelsHigh]
															   destinationWidthInUnits: self.destinationWidthInUnits
															  destinationHeightInUnits: self.destinationHeightInUnits
																	destinationXOffset: self.destinationXOffsetInUnits
																	destinationYOffset: self.destinationYOffsetInUnits
										]
								atIndex:columnIndex];
			
			// We don't need this cell anymore
			[newCell release];
		}
		// Store this row of cells
		[newCells insertObject:rowOfNewCells
							  atIndex:rowIndex];
		// Release the row
		[rowOfNewCells release];
	}
	
	// Release the buffer representation and handle errors 
	[imageToUse unlockBufferRepresentation];
	
	// Return nothing if there was an error
	if (error)
		return nil;
	
	// Return all the cells
	return newCells;
}



// Returns an NSArray representing the grid and its cells
- (NSArray*) asNSArray
{
	return self.cells;
}

// Return a description of this grid
- (NSString*) description
{
	NSMutableString* description = [[NSMutableString alloc] initWithString:@"Grid Instance"];
	[description appendFormat:@"\n\tNumber of columns: %i", self.numberOfColumns];
	[description appendFormat:@"\n\tNumber of rows: %i", self.numberOfRows];
	[description appendFormat:@"\n\tWidth in pixels: %i", self.widthInPixels];
	[description appendFormat:@"\n\tHeight in pixels: %i", self.heightInPixels];
	[description appendFormat:@"\n\tNumber of cells: %i by %i", [cells count], [[cells lastObject] count]];
	return description;
}

@end
